#include "ExpenseSystem.h"
using namespace std;

	ExpenseSystem :: ExpenseSystem (string username, double totalBudget)
	{
		expenseCategory.push_back(Category("Food"));
		expenseCategory.push_back(Category("Transportation"));
		expenseCategory.push_back(Category("School/Office"));
		expenseCategory.push_back(Category("Entertainment"));
		expenseCategory.push_back(Category("Shopping"));
		expenseCategory.push_back(Category("Miscellanous"));
		_totalBudget=totalBudget;
		_username=username;
		
	}

	ExpenseSystem :: ~ExpenseSystem()
	{
		storeCategory();
		storeItem();
	}
	
	void ExpenseSystem :: addCategory(string name)
	{
		Step *ptr;
		ptr=new Step;
		ptr->operation="deleteCategory";
		ptr->Categoryname=name;
		ptr->newName=name;
		Backup.push(*ptr);
		expenseCategory.push_back(Category(name));
		return;
	}

	void ExpenseSystem :: updateCategory(string currentName, string newName)
	{
		Step *ptr;
		ptr=new Step;
		ptr->operation="updateCategory";
		ptr->Categoryname=newName;
		ptr->newName=currentName;

		expenseCategory[findCategory(currentName)].updateName(newName);
		return;
	}

	void  ExpenseSystem :: deleteCategory(string name)
	{
		Step *ptr;
		ptr=new Step;
		ptr->operation="addCategory";
		ptr->Categoryname=name;
		ptr->_size=expenseCategory[findCategory(name)].getExpenseItem().size();
		ptr->newName=name;
		for(int i=ptr->_size-1;i>=0;i--)
		{
			ptr->cItem.push(expenseCategory[findCategory(name)].getExpenseItem().at(i));

		}
		Backup.push(*ptr);

		int idx = findCategory(name);
		if(idx<expenseCategory.size())
		{
			vector<Category> :: iterator myIter=expenseCategory.begin();
			for(int i=0;i<idx;i++)
				myIter++;
			expenseCategory.erase(myIter);
		}
	}
	
	void  ExpenseSystem :: addItem(string category, DateTime date, string name, double amount, PaymentStatus status)
	{
		expenseCategory[findCategory(category)].addItem(date,name,amount, status);
		
		Step *ptr;
		ptr=new Step;
		ptr->operation="deleteItem";
		ptr->Categoryname=category;
		ptr->Itemindex=expenseCategory[findCategory(category)].getExpenseItem().size()-1;
		ptr->newName=category;
		ptr->_size=0;
		Backup.push(*ptr);
		
		
		return;
	}
	
	void ExpenseSystem :: updateItem(string category, int index, DateTime date, string name, double amount, PaymentStatus status)
	{
		Step* ptr;
		ptr=new Step;
		ptr->operation="updateItem";
		ptr->Categoryname=category;
		ptr->Itemindex=index;
		ptr->newName=category;
		ptr->cItem.push(expenseCategory[findCategory(category)].getExpenseItem().at(index));
		ptr->_size=0;
		Backup.push(*ptr);


		expenseCategory[findCategory(category)].updateItem(index, date, name, amount, status);
		return;
	}

	void  ExpenseSystem :: deleteItem(string category, int index)
	{
		Step* ptr;
		ptr=new Step;
		ptr->operation="addItem";
		ptr->Categoryname=category;
		ptr->Itemindex=index;
		ptr->newName=category;
		ptr->cItem.push(expenseCategory[findCategory(category)].getExpenseItem().at(index));
		ptr->_size=1;
		Backup.push(*ptr);

		expenseCategory[findCategory(category)].deleteItem(index);
		return;
	}

	void ExpenseSystem :: search(string category, DateTime date)
	{
		printSearchResult(category, expenseCategory[findCategory(category)].search(date));
		return;
	}

	void ExpenseSystem :: printSearchResult(string category, const vector<int>& result)

	{
		cout << expenseCategory[findCategory(category)].toString(result);
		return;
	}

    void ExpenseSystem:: undo()
	{
		if(Backup.top().operation=="addItem")
		{
			expenseCategory[findCategory(Backup.top().Categoryname)].insertItem(Backup.top().cItem.top(),Backup.top().Itemindex);
		}
		else if(Backup.top().operation=="deleteItem")
		{
			deleteItem(Backup.top().Categoryname,Backup.top().Itemindex);
		}
		else if(Backup.top().operation=="updateItem")
		{
			updateItem(Backup.top().Categoryname,Backup.top().Itemindex,Backup.top().cItem.top().getDateTime(),Backup.top().cItem.top().getName(),Backup.top().cItem.top().getAmount(),Backup.top().cItem.top().getStatus());
		}
		else if(Backup.top().operation=="addCategory")
		{
			addCategory(Backup.top().Categoryname);
			for(int i=0;i<Backup.top()._size;i++)
			{
				expenseCategory[findCategory(Backup.top().Categoryname)].insertItem(Backup.top().cItem.top(),i);

			}
		}
		else if(Backup.top().operation=="deleteCategory")
		{
			deleteCategory(Backup.top().Categoryname);

		}
		else if(Backup.top().operation=="updateCatagory")
		{
			updateCategory(Backup.top().Categoryname,Backup.top().newName);
		}
		Backup.pop();
	}
	
	void ExpenseSystem :: calcUsedBudget(const DateTime& date, double& totalUsed, vector<pair<string,double>>& usedBudget)
	{
		totalUsed=0;
		for(int i=0;i<expenseCategory.size();i++)
		{
			double amount=0;
			expenseCategory[i].getUsedBudget(date);
			usedBudget.push_back(pair<string,double>(expenseCategory[i].getName(), amount));
			totalUsed+=amount;
		}

		return;
	}

	void ExpenseSystem :: printReport(const DateTime& date)
	{
		printExpense(date);
		printPayable("report");
		return;
	}

	void ExpenseSystem :: printExpense(const DateTime& date)
	{
		double totalUsed;
		vector<pair<string,double>> usedBudget;
		calcUsedBudget(date, totalUsed, usedBudget);
		cout<<"Monthly Report on "<<"/"<<date.month<<"/"<<date.year<<endl;
		for(unsigned int i=0;i<usedBudget.size();i++)
		{
			cout<<setw(20)<<usedBudget[i].first<<"$"<<usedBudget[i].second<<endl;
			cout<<setw(20)<<"Total"<<totalUsed<<endl;
			cout<<setw(20)<<"Total Budget "<<"$"<<_totalBudget<<endl;
		}
		return;
	}

	//purpose can be report or alert
	void ExpenseSystem::printPayable(const string& purpose)
	{
		vector<int> result;
		double totalAmount=0;
		int nPayable=0;
		for(unsigned int i=0;i<expenseCategory.size();i++)
		{
			double amount=0;
			expenseCategory[i].calculatePayable(amount, result);
			
			totalAmount+=amount;
			nPayable+=result.size();
			result.clear();

			if(purpose=="report")
			{
				cout << "Payable Report" << endl;
				cout << expenseCategory[i].toString(result);	
			}
		}
		cout<< " You have " << nPayable << "outstanding payables with total payable "<<"$"<<totalAmount<<endl;
		return;
	}

	void ExpenseSystem :: storeCategory()
	{
		string categoryName;
		string fileName=_username+"-CategoryList";
		ofstream writeFile(fileName.c_str());
		for(unsigned int i=0;i<expenseCategory.size();i++)
			writeFile<<expenseCategory[i].getName()<<endl;
		writeFile.close();
		return;
	}
	
	void ExpenseSystem :: storeItem()
	{	
		for(unsigned int i=0;i<expenseCategory.size();i++){
		string file_name = _username+"-"+expenseCategory[i].getName();
		ofstream writeFile(file_name.c_str());
		writeFile<<expenseCategory[i].toString();
		writeFile.close();
		}
		return;
	}

	void ExpenseSystem :: restoreCategory()
	{
		string categoryName;
		string fileName=_username+"-CategoryList";
		ifstream readFile(fileName.c_str());
		while(readFile>>categoryName)
			addCategory(categoryName);
		readFile.close();
	}
	
	void ExpenseSystem :: restoreItem()
	{
		string oneline;
		string name;
		double amount;
		DateTime date;
		string statusString;
		PaymentStatus status;

		static std::pair<const char*, PaymentStatus> pairArr[4];
		pairArr[0] = make_pair( "PAID", PAID);
		pairArr [1] = make_pair("PAYABLE", PAYABLE); 
		
		for(unsigned int i=0;i<expenseCategory.size();i++){
		string file_name = _username+"-"+expenseCategory[i].getName();
		ifstream readFile;
		readFile.open(file_name.c_str());
		while( getline(readFile,oneline) )
		{
			istringstream iss(oneline);
			iss >> date.day >> date.month >> date.year >>name>>amount>>statusString;
			//status = myConvert.string2status(statusString);
		 for (int j = 0; j < sizeof pairArr/sizeof pairArr[0]; ++j)
		{
		if (statusString == pairArr[i].first)
			status= pairArr[i].second;
		 }
			expenseCategory[i].addItem(date,name,amount,status);
		}
		readFile.close();
	}
		return;
	}